﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Elderos.Highlight.Rules;
using Elderos.Ontology;
using Elderos.Utils;

namespace Elderos.Highlight
{
    public class RuleRanker : IRanker
    {
        private readonly IOntologyData _ontology;

        public IEnumerable<RankingRule> RankingRules { get; private set; }

        private readonly List<Func<HighlightResult, Task>> _articleCacheInit = new List<Func<HighlightResult, Task>>();        

        public RuleRanker(IOntologyData ontologyData, Predicate<RankingRule> filter = null)
        {
            if (filter == null)
                filter = x => true;

            _ontology = ontologyData;
            RankingRules = GetRules()
                .Where(x => filter(x))
                .ToArray();

            FeatureMap = new Dictionary<string, int>();

            int counter = 0;

            foreach (var rankingRule in RankingRules)
            {
                rankingRule.Initialize(_articleCacheInit).Wait();

                foreach (var factorValue in rankingRule.EmptyResult.GetFactorValues())
                {
                    FeatureMap.Add(factorValue.FactorName, counter);
                    counter++;
                }
            }
        }

        private IEnumerable<RankingRule> GetRules()
        {
            var types = GetRuleTypes();
            return from type in types
                   let ctor = type.GetConstructor(new[] { typeof(IOntologyData) })
                   where ctor != null
                   select (RankingRule)ctor.Invoke(new[] { _ontology });
        }

        public static IEnumerable<Type> GetRuleTypes()
        {
            var asm = Assembly.GetAssembly(typeof(RankingRule));
            var types =
                asm.GetTypes().Where(
                    x =>
                    x.IsSubclassOf(typeof(RankingRule))
                    && x.GetCustomAttributes(typeof(RankingRuleAttribute), true)
                           .Any(y => ((RankingRuleAttribute) y).IsActive));
            return types;
        }

        public async void Rank(HighlightResult result)
        {
            foreach (var initCacheAction in _articleCacheInit)
                await initCacheAction(result);

            foreach (var rule in RankingRules)
                await rule.Rank(result);
        }

        public Dictionary<string, int> FeatureMap { get; private set; }
    }
}
